package v4_param_route

import (
	"fmt"
	"reflect"
	"strings"
)

type router struct {
	trees map[string]*node // 按照 HTTP 方法来组织的路由树 GET -> *node
}

// addRoute 注册路由的方法
// 1.排除掉非法的路由;2.创建路由
func (r *router) addRoute(method string, path string, handler HandleFunc) {
	// 1.排除掉非法的路由
	if path == "" {
		panic("web: 路由是空字符串")
	}
	if path[0] != '/' {
		panic("web: 路由必须以 / 开头")
	}
	if path != "/" && path[len(path)-1] == '/' {
		panic("web: 路由不能以 / 结尾")
	}
	if strings.Contains(path, "//") {
		panic(fmt.Sprintf("web: 非法路由。不允许使用 //a/b, /a//b 之类的路由, [%s]", path))
	}
	// 特殊处理'/'
	root, ok := r.trees[method]
	if !ok { // 特殊处理每个方法的 '/'
		// 没有的时候需要创建根节点
		root = &node{path: "/"}
		r.trees[method] = root
	}
	if path == "/" {
		if root.handler != nil {
			panic("web: 路由冲突[/]")
		}
		root.handler = handler
		return
	}
	// 2.创建路由
	splits := strings.Split(path[1:], "/")
	for _, s := range splits { // 开始一段段处理
		root = root.childOrCreate(s)
	}
	if root.handler != nil {
		panic(fmt.Sprintf("web: 路由冲突[%s]", path))
	}
	root.handler = handler // 最后将路径的 handler 注册到路由树上
}

// findRoute 查找路由的方法
// 注意，返回的 node 内部 HandleFunc 不为 nil 才算是注册了路由
// 因为注册路由时已经校验过路由的合法性
func (r *router) findRoute(method string, path string) (*matchInfo, bool) {
	curNode, ok := r.trees[method]
	if !ok {
		return nil, false
	}
	if path == "/" {
		return &matchInfo{n: curNode}, true
	}

	splits := strings.Split(strings.Trim(path, "/"), "/")
	mi := &matchInfo{}
	for _, p := range splits {
		var matchParam bool
		curNode, matchParam, ok = curNode.childOf(p)
		if !ok {
			return nil, false
		}
		if matchParam {
			mi.addValue(curNode.path[1:], p)
		}
	}
	mi.n = curNode
	return mi, true

}

func (r router) equal(y router) (string, bool) {
	for k, v := range r.trees {
		yv, ok := y.trees[k]
		if !ok {
			return fmt.Sprintf("目标 router 里面没有方法 %s 的路由树", k), false
		}
		str, ok := v.equal(yv)
		if !ok {
			return k + "-" + str, ok
		}
	}
	return "", true
}

type node struct {
	// 优先级:静态匹配 > 参数路径匹配 > 通配符匹配 (不回溯匹配)
	path       string
	handler    HandleFunc
	children   map[string]*node // 静态子节点
	starChild  *node            // 通配符子节点
	paramChild *node            // 参数路径子节点
}

func (n *node) equal(y *node) (string, bool) {
	if y == nil {
		return "目标节点为 nil", false
	}

	if n.path != y.path {
		return fmt.Sprintf("%s 节点 path 不相等 x %s, y %s", n.path, n.path, y.path), false
	}

	nhv := reflect.ValueOf(n.handler)
	yhv := reflect.ValueOf(y.handler)
	if nhv != yhv {
		return fmt.Sprintf("%s 节点 handler 不相等 x %s, y %s", n.path, nhv.Type().String(), yhv.Type().String()), false
	}

	if len(n.children) != len(y.children) {
		return fmt.Sprintf("%s 子节点长度不等", n.path), false
	}
	if len(n.children) == 0 {
		return "", true
	}

	if n.starChild != nil {
		starRes, ok := n.starChild.equal(y.starChild)
		if !ok {
			return fmt.Sprintf("%s 通配符节点不匹配 %s", n.path, starRes), false
		}
	}

	if n.paramChild != nil {
		paramRes, ok := n.paramChild.equal(y.paramChild)
		if !ok {
			return fmt.Sprintf("%s 参数路径节点不匹配 %s", n.path, paramRes), false
		}
	}

	for k, v := range n.children {
		yv, ok := y.children[k]
		if !ok {
			return fmt.Sprintf("%s 目标节点缺少子节点 %s", n.path, k), false
		}
		str, ok := v.equal(yv)
		if !ok {
			return n.path + "-" + str, ok
		}
	}
	return "", true
}

// childOrCreate 查找子节点，如果子节点不存在就创建一个
// 并且将子节点放回去了 children 中
func (n *node) childOrCreate(path string) *node {
	// 通配符单和路径参数是互斥的
	if path == "*" {
		if n.paramChild != nil {
			panic(fmt.Sprintf("web: 非法路由，已有路径参数路由。不允许同时注册通配符路由和参数路由 [%s]", path))
		}
		if n.starChild == nil {
			n.starChild = &node{path: path}
		}
		return n.starChild
	}
	if path[0] == ':' { // 以 : 开头，认为是参数路由
		if n.starChild != nil {
			panic(fmt.Sprintf("web: 非法路由，已有通配符路由。不允许同时注册通配符路由和参数路由 [%s]", path))
		}
		if n.paramChild != nil {
			if n.paramChild.path != path {
				panic(fmt.Sprintf("web: 路由冲突，参数路由冲突，已有 %s，新注册 %s", n.paramChild.path, path))
			}
		} else {
			n.paramChild = &node{path: path}
		}
		return n.paramChild
	}
	if n.children == nil {
		n.children = make(map[string]*node)
	}

	child, ok := n.children[path]
	if !ok {
		child = &node{path: path}
		n.children[path] = child
	}
	return child
}

// childOf 返回子节点
// 第一个返回值 *node 是命中的节点
// 第二个返回值 bool 代表是否是命中参数路由
// 第三个返回值 bool 代表是否命中
func (n *node) childOf(path string) (*node, bool, bool) {
	if n.children == nil {
		if n.paramChild != nil {
			return n.paramChild, true, true
		}
		return n.starChild, false, n.starChild != nil
	}
	child, ok := n.children[path]
	if !ok {
		if n.paramChild != nil {
			return n.paramChild, true, true
		}
		return n.starChild, false, n.starChild != nil
	}
	return child, false, ok
}

type matchInfo struct {
	n          *node
	pathParams map[string]string
}

func (m *matchInfo) addValue(key string, value string) {
	if m.pathParams == nil {
		// 大多数情况，参数路径只会有一段
		m.pathParams = map[string]string{key: value}
	}
	m.pathParams[key] = value
}

func newRouter() router {
	return router{
		trees: map[string]*node{},
	}
}
